package com.duoduo.common.api.help;


import com.duoduo.common.api.constants.Constant;
import com.duoduo.common.api.tuple.Tuple;
import com.duoduo.common.api.tuple.TwoTuple;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;

/**
 * spring切面帮助类
 *
 * @author dell
 */
public class JoinPointInnerUtil {

    private static final Logger logger = LoggerFactory.getLogger(JoinPointInnerUtil.class);

    /**
     * 获取被拦截方法的参数名
     *
     * @param joinpoint
     * @return
     */
    public static String[] getParamterNames(JoinPoint joinpoint) {
        LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
        return u.getParameterNames(getRealMethod(joinpoint));
    }

    /**
     * 方法描述：判断指定对象上是否有指定注解，如果是spring代理的对象，需要找到真实对象
     *
     * @param joinpoint
     * @return
     */
    public static <T extends Annotation> Boolean getRealClassAnnotation(Object obj, Class<T> anno) {
        if (!AopUtils.isAopProxy(obj)) {
            return obj.getClass().isAnnotationPresent(anno);//不是代理对象
        }
        Class<?> realClass = AopUtils.getTargetClass(obj);
        return realClass.isAnnotationPresent(anno);
    }

    /**
     * 方法描述：获取拦截方法的签名
     *
     * @param joinpoint
     * @return
     */
    public static String getMethodName(JoinPoint joinpoint) {
        Method method = getRealMethod(joinpoint);
        return method.getName();
    }

    /**
     * 方法描述：获取拦截方法的注解
     *
     * @param joinpoint
     * @return
     */
    public static Annotation[] getMethodAnnotations(JoinPoint joinpoint) {
        Method method = getRealMethod(joinpoint);
        return method.getAnnotations();
    }

    /**
     * 方法描述：获取拦截方法的指定注解
     *
     * @param joinpoint
     * @return
     */
    public static <T extends Annotation> T getMethodAnnotation(JoinPoint joinpoint, Class<T> anno) {
        Method method = getRealMethod(joinpoint);
        T annotation = method.getAnnotation(anno);
        if (annotation == null) {
            Signature signature = joinpoint.getSignature();
            if (signature instanceof MethodSignature) {
                annotation = ((MethodSignature) signature).getMethod().getAnnotation(anno);
            }
        }
        return annotation;
    }

    /**
     * 获取方法的参数上的注解
     *
     * @param joinpoint
     * @return
     */
    public static Annotation[][] getMethodParamAnnos(JoinPoint joinpoint) {
        Method method = getRealMethod(joinpoint);
        return method.getParameterAnnotations();
    }

    /**
     * 方法描述：获取被拦截类自身定义的所有方法对象
     *
     * @param joinpoint
     * @return
     */
    public static Method[] getMethods(JoinPoint joinpoint) {
        Class<?> clazz = joinpoint.getTarget().getClass();
        return clazz.getDeclaredMethods();
    }

    /**
     * 获取被拦截方法对应的单个执行方法
     *
     * @param joinpoint
     * @param methodName
     * @param inputReturnClass 方法输入、输出参数类型
     * @return
     * @throws NoSuchMethodException
     * @throws IllegalAccessException
     */
    public Method getMethod(JoinPoint joinpoint, String methodName, Class<?>... inputReturnClass) throws NoSuchMethodException, IllegalAccessException {
        Object obj = JoinPointInnerUtil.getClass(joinpoint);
        return obj.getClass().getDeclaredMethod(methodName, inputReturnClass);
    }

    /**
     * 方法描述：获取被拦截类的类名
     *
     * @param joinpoint
     * @return
     */
    public static String getClassName(JoinPoint joinpoint) {
        Class<?> clazz = joinpoint.getTarget().getClass();
        return clazz.getName();
    }

    /**
     * 方法描述：获取被拦截类的类名
     *
     * @param joinpoint
     * @return
     */
    public static String getClassSimpleName(JoinPoint joinpoint) {
        Class<?> clazz = joinpoint.getTarget().getClass();
        return clazz.getSimpleName();
    }

    /**
     * 方法描述：获取被拦截的类
     *
     * @param joinpoint
     * @return
     */
    public static Object getClass(JoinPoint joinpoint) {
        return joinpoint.getTarget();
    }

    /**
     * 方法描述：获取被拦截的代理类
     *
     * @param joinpoint
     * @return
     */
    public static Object getProxy(JoinPoint joinpoint) {
        return joinpoint.getThis();
    }

    /**
     * 获取被拦截的类的属性值
     *
     * @param joinpoint
     * @param attributeName 属性名
     * @return
     */
    public static Object getAttribute(JoinPoint joinpoint, String attributeName) {
        try {
            Field f = joinpoint.getTarget().getClass().getDeclaredField(attributeName);//获取属性-->Field
            f.setAccessible(true);//如果是私有的 先要设置可访问
            return f.get(joinpoint.getTarget());//获取值,这个get()方法重点是参数,参数是你要获取的类
        } catch (Exception e) {
            try {
                Field f = joinpoint.getTarget().getClass().getSuperclass().getDeclaredField(attributeName);//获取父类属性-->Field
                f.setAccessible(true);
                return f.get(joinpoint.getTarget());
            } catch (Exception e2) {
                e2.printStackTrace();
                return null;
            }
        }
    }

    /**
     * 获取被拦截的参数的属性值
     *
     * @param param         被拦截的参数
     * @param attributeName 属性名
     * @return
     */
    public static Object getAttribute(Object param, String attributeName) {
        try {
            Field f = param.getClass().getDeclaredField(attributeName);//获取属性-->Field
            f.setAccessible(true);//如果是私有的 先要设置可访问
            return f.get(param);//获取值,这个get()方法重点是参数,参数是你要获取的类
        } catch (Exception e) {
            try {
                Field f = param.getClass().getSuperclass().getDeclaredField(attributeName);//获取父类属性-->Field
                f.setAccessible(true);
                return f.get(param);
            } catch (Exception e2) {
                e2.printStackTrace();
                return null;
            }
        }
    }

    /**
     * 设置被拦截的参数的属性值
     *
     * @param param         被拦截的参数
     * @param attributeName 属性名
     * @return
     */
    public static Object setAttribute(Object param, String attributeName, Object newValue) {
        try {
            Field f = param.getClass().getDeclaredField(attributeName);
            f.setAccessible(true);
            f.set(param, newValue);
            return param;
        } catch (Exception e) {
            try {
                Field f = param.getClass().getSuperclass().getDeclaredField(attributeName);//获取父类属性-->Field
                f.setAccessible(true);
                f.set(param, newValue);
                return param;
            } catch (Exception e2) {
                e2.printStackTrace();
                return null;
            }
        }
    }

    /**
     * 方法描述：获取被拦截类的被拦截方法对象
     *
     * @param joinpoint
     * @return 当找不到对应方法时，返回null
     */
    public static Method getRealMethod(JoinPoint joinpoint) {
        Signature signature = joinpoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method interfaceMethod = methodSignature.getMethod();
        try {
            return joinpoint.getTarget().getClass().getMethod(signature.getName(), interfaceMethod.getParameterTypes());
        } catch (NoSuchMethodException | SecurityException e) {
            logger.error(Constant.EMPTY_STRING, e);
            return null;
        }
    }

    /**
     * 方法描述：获取被拦截类的被拦截方法的返回值类型
     *
     * @param joinpoint
     * @return
     */
    public static Class<?> getMethodReturnType(JoinPoint joinpoint) {
        Method method = getRealMethod(joinpoint);
        return (Class<?>) method.getReturnType();
    }

    /**
     * 方法描述：获取被拦截类的被拦截方法的返回值类型，带泛型
     *
     * @param joinpoint
     * @return 如果没有泛型，返回null
     */
    public static ParameterizedType getMethodGenericReturnType(JoinPoint joinpoint) {
        Method method = getRealMethod(joinpoint);
        if (method.getGenericReturnType() instanceof ParameterizedType)
            return (ParameterizedType) method.getGenericReturnType();
        return null;
    }

    /**
     * 方法描述：获取拦截方法的所有输入参数值
     *
     * @param joinpoint
     * @return
     */
    public static Object[] getMethodArgValues(JoinPoint joinpoint) {
        return joinpoint.getArgs();
    }

    /**
     * 方法描述：获取拦截方法的输入参数，判断是否有指定的注解，如果有，返回注解和该参数值
     *
     * @param joinpoint
     * @param anno      指定的注解
     * @return
     */
    public static TwoTuple<Annotation, Object> getMethodArgValuesByAnnotation(JoinPoint joinpoint, Class<? extends Annotation> annoClass) {
        Object[] objs = getMethodArgValues(joinpoint);
        Annotation[][] annss = getMethodParamAnnos(joinpoint);
        for (int i = 0; i < annss.length; i++) {
            Annotation[] annotations = annss[i];
            for (Annotation annotation : annotations) {
                if (annoClass.equals(annotation.annotationType())) {
                    return Tuple.tuple(annotation, objs[i]);
                }
            }
        }
        return null;
    }

    /**
     * 方法描述：获取拦截方法的输入参数，判断是否有指定的注解，如果有，返回注解和该参数值
     *
     * @param annss 参数上的注解
     * @param objs  参数值
     * @param anno  指定的注解
     * @return
     */
    public static TwoTuple<Annotation, Object> getMethodArgValuesByAnnotation(Annotation[][] annss, Object[] objs, Class<? extends Annotation> annoClass) {
        for (int i = 0; i < annss.length; i++) {
            Annotation[] annotations = annss[i];
            for (Annotation annotation : annotations) {
                if (annoClass.equals(annotation.annotationType())) {
                    return Tuple.tuple(annotation, objs[i]);
                }
            }
        }
        return null;
    }

    /**
     * @param joinpoint
     * @param annoClass
     * @description: 方法描述：获取拦截方法的输入参数，判断是否有指定的注解，如果有，返回注解和该参数值
     * @return: java.util.List<com.duoduo.common.api.tuple.TwoTuple < java.lang.annotation.Annotation, java.lang.Object>>
     * @date: 2022/8/26 13:38
     */
    public static List<TwoTuple<Annotation, Object>> getMethodArgValuesByAnnotations(JoinPoint joinpoint, Class<? extends Annotation> annoClass) {
        List<TwoTuple<Annotation, Object>> result = new ArrayList<>();
        Object[] objs = getMethodArgValues(joinpoint);
        Annotation[][] annss = getMethodParamAnnos(joinpoint);
        for (int i = 0; i < annss.length; i++) {
            Annotation[] annotations = annss[i];
            for (Annotation annotation : annotations) {
                if (annoClass.equals(annotation.annotationType())) {
                    result.add(Tuple.tuple(annotation, objs[i]));
                }
            }
        }
        return result;
    }

    /**
     * 方法描述：获取拦截方法的输入参数，判断是否有指定的注解，如果有，返回注解和该参数值
     *
     * @param joinpoint
     * @param anno      指定的注解
     * @return
     */
    public static List<TwoTuple<Annotation, Object>> getMethodArgValuesByMultiAnnotations(JoinPoint joinpoint, List<Class<? extends Annotation>> annoClassList) {
        List<TwoTuple<Annotation, Object>> result = new ArrayList<>();
        Object[] objs = getMethodArgValues(joinpoint);
        Annotation[][] annss = getMethodParamAnnos(joinpoint);
        for (int i = 0; i < annss.length; i++) {
            Annotation[] annotations = annss[i];
            for (Annotation annotation : annotations) {
                if (annoClassList.contains(annotation.annotationType())) {
                    result.add(Tuple.tuple(annotation, objs[i]));
                }
            }
        }
        return result;
    }

    /**
     * 获取拦截方法的输入参数，判断是否有指定的注解，如果有，用value替换被注解的字段值
     *
     * @param joinpoint
     * @param class1
     * @param value
     */
    public static void setMethodArgValuesByAnnotation(JoinPoint joinpoint, Class<? extends Annotation> annoClass, String value) {
        Object[] objs = getMethodArgValues(joinpoint);
        Annotation[][] annss = getMethodParamAnnos(joinpoint);
        for (int i = 0; i < annss.length; i++) {
            Annotation[] annotations = annss[i];
            for (Annotation annotation : annotations) {
                if (annoClass.equals(annotation.annotationType())) {
                    objs[i] = value;
                }
            }
        }
    }

    /**
     * 获取拦截方法的输入参数，判断是否有指定的注解，如果有，执行func更新指定值
     *
     * @param joinpoint
     * @param class1
     * @param value
     */
    public static <T, U> Object[] setMethodArgValuesByAnnotation(JoinPoint joinpoint, Class<? extends Annotation> annoClass, Function<T, U> func, T value) {
        Object[] objs = getMethodArgValues(joinpoint);
        Annotation[][] annss = getMethodParamAnnos(joinpoint);
        for (int i = 0; i < annss.length; i++) {
            Annotation[] annotations = annss[i];
            for (Annotation annotation : annotations) {
                if (annoClass.equals(annotation.annotationType())) {
                    objs[i] = func.apply(value);
                }
            }
        }
        return objs;
    }

    public static String getClassAndMethodName(ProceedingJoinPoint joinpoint) {
        String className = getClassName(joinpoint);
        String shortClassName = className.substring(className.lastIndexOf(".") + 1);
        String methodName = getMethodName(joinpoint);
        return shortClassName + "." + methodName;
    }

    /**
     * 获取实现的接口的泛型
     *
     * @param obj
     * @return
     */
    public static Class<?> getInterfaceTypeClass(Object obj) {
        Class<?> clazz = (Class<?>) ((ParameterizedType) obj.getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0];
        return clazz;
    }

    /**
     * 是否所有参数都没有实现序列化
     *
     * @param joinpoint
     * @return
     */
    public static boolean isAllNotSerializable(ProceedingJoinPoint joinpoint) {
        Object[] objs = JoinPointInnerUtil.getMethodArgValues(joinpoint);
        return !Arrays.stream(objs).filter(obj -> obj instanceof Serializable).findAny().isPresent();
    }

}
